/******************************************************************
 * 个人博客：https://blog.csdn.net/2302_80277720
 * 嵌入式Linux学习交流群：1005210698
 * 欢迎各位大佬和萌新来加入交流学习
 * Change Logs:
 * Date           Author       Notes
 * 2025-08-24     喝呜昂黄    first version
 * 2025-08-30     喝呜昂黄    修改页翻转，添加日志功能
 ******************************************************************/
#include <pthread.h>
#include <signal.h>
#include <stdint.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <sys/time.h>
#include <unistd.h>

#include "common.h"
#include "convert.h"
#include "disp_manager.h"
#include "image_drawing.h"
#include "video_manager.h"
#include "yolov8.h"

static int quit = 0;

static void logger_init(void) {
    logger_set_level(hlog, LOG_LEVEL_VERBOSE);
    logger_set_handler(hlog, stdout_logger);
    logger_enable_color(hlog, 1);

    logger_set_format(hlog, "%s");
    logger_print(hlog, LOG_LEVEL_INFO,
        " █████   █████                                     \n"
        "░░███   ░░███                                          \n"
        " ░███    ░███  █████ ████  ██████   ████████    ███████\n"
        " ░███████████ ░░███ ░███  ░░░░░███ ░░███░░███  ███░░███\n"
        " ░███░░░░░███  ░███ ░███   ███████  ░███ ░███ ░███ ░███\n"
        " ░███    ░███  ░███ ░███  ███░░███  ░███ ░███ ░███ ░███\n"
        " █████   █████ ░░████████░░████████ ████ █████░░███████\n"
        "░░░░░   ░░░░░   ░░░░░░░░  ░░░░░░░░ ░░░░ ░░░░░  ░░░░░███\n"
        "                                               ███ ░███\n"
        "                                              ░░██████ \n"
        "                                               ░░░░░░  \n");

    logger_print(hlog, LOG_LEVEL_INFO, "Version:%s  Build time: %s\n", PROJECT_VERSION, PROJECT_BUILD_TIME);
    logger_set_format(hlog, DEFAULT_LOG_FORMAT);
}

/* ========================= drm module start ========================= */
static drm_device_t drm_device;
/* ========================= drm module end ========================= */

/* ========================= rknn module start ========================= */
static rknn_app_context_t rknn_app_ctx;
static object_detect_result_list od_results;

static int rknn_init(const char *model_path) {
    int ret;

    memset(&rknn_app_ctx, 0, sizeof(rknn_app_context_t));

    init_post_process();
    LOG_INFO("init_post_process success");

    ret = init_yolov8_model(model_path, &rknn_app_ctx);
    if (ret != 0) {
        LOG_ERROR("init_yolov8_model fail! ret=%d model_path=%s", ret, model_path);
        return -1;
    }

    return 0;
}

static void rknn_destroy(void) {
    int ret;
    deinit_post_process();

    ret = release_yolov8_model(&rknn_app_ctx);
    if (ret != 0)
        LOG_ERROR("release_yolov8_model fail! ret=%d", ret);
}

static int model_inference(image_buffer_t *src_image) {
    int ret;

    ret = inference_yolov8_model(&rknn_app_ctx, src_image, &od_results);
    if (ret != 0) {
        LOG_ERROR("init_yolov8_model fail! ret=%d", ret);
        return -1;
    }

    // 画框和概率
    char text[256];
    for (int i = 0; i < od_results.count; i++) {
        object_detect_result *det_result = &(od_results.results[i]);
        LOG_INFO("%s @ (%d %d %d %d) %.3f", coco_cls_to_name(det_result->cls_id), det_result->box.left, det_result->box.top, det_result->box.right,
            det_result->box.bottom, det_result->prop);
        int x1 = det_result->box.left;
        int y1 = det_result->box.top;
        int x2 = det_result->box.right;
        int y2 = det_result->box.bottom;

        draw_rectangle(src_image, x1, y1, x2 - x1, y2 - y1, COLOR_BLUE, 3);

        sprintf(text, "%s %.1f%%", coco_cls_to_name(det_result->cls_id), det_result->prop * 100);
        draw_text(src_image, text, x1, y1 - 20, COLOR_RED, 10);
    }
}
/* ========================= rknn module end ========================= */

/* ========================= camera module start ========================= */
static video_dev_t mipi_video;
static video_opr_t *mipi_video_opr;
struct timeval start_time, end_time;
static void *mipi_thread_func(void *param) {
    video_dev_t *mipi_video = (video_dev_t *)param;
    mipi_video_opr          = mipi_video->video_opr;

    while (mipi_video->thread_run) {
        mipi_video_opr->get_frame(mipi_video);

        uint8_t back_idx = 1 - drm_device.current_idx;

        // RGA Rotation `IM_HAL_TRANSFORM_ROT_90`
        covert(&mipi_video->mmap_buf[mipi_video->index], &drm_device.disp_image[back_idx], 0);

        model_inference(&drm_device.disp_image[back_idx]);

        if (drm_device.flip_pending == 0) {
            drm_device.planes_setting.fb_id = drm_device.fd_ids[back_idx];
            drm_request_page_flip(&drm_device);
        }

        usleep(20000);

        mipi_video_opr->put_frame(mipi_video);
    }
    mipi_video_opr->dev_stop(mipi_video);

    return NULL;
}

/* ========================= camera module end ========================= */

static void signal_head(int sig) {
    quit = 1;
    LOG_INFO("%d is signal", sig);
}

int main(int argc, char **argv) {
    int ret;

    logger_init();

    if (argc != 2) {
        LOG_WARN("%s <model_path>", argv[0]);
        return -1;
    }

    /* ========================= rknn module end ========================= */
    const char *model_path = argv[1];

    ret = rknn_init(model_path);
    if (ret < 0) {
        LOG_ERROR("rknn_init fail");
        goto rknn_err;
    }
    LOG_INFO("rknn_init success");
    /* ========================= rknn module end ========================= */

    /* ========================= drm module start ========================= */
    CLEAR(drm_device);

    for (int i = 0; i < 2; i++) {
        drm_device.disp_image[i].width  = 640;
        drm_device.disp_image[i].height = 640;
        drm_device.disp_image[i].bpp    = 24;
        drm_device.disp_image[i].format = IMAGE_FORMAT_RGB888;
    }

    ret = drm_init(&drm_device);
    if (ret < 0) {
        LOG_ERROR("DRM 初始化失败");
        goto rknn_err;
    }

    drm_get_plane_property(&drm_device.property_planes, drm_device.plane_id);

    // 配置平面
    drm_device.planes_setting.plane_id = drm_device.plane_id;
    drm_device.planes_setting.fb_id    = drm_device.fd_ids[drm_device.current_idx];
    // 平面在CRTC上的显示位置和大小
    drm_device.planes_setting.crtc_x = 40;
    drm_device.planes_setting.crtc_y = 320;
    drm_device.planes_setting.crtc_w = drm_device.disp_image->width;
    drm_device.planes_setting.crtc_h = drm_device.disp_image->height;
    // 从帧缓冲中取图的区域（全图）
    drm_device.planes_setting.src_x = 0;
    drm_device.planes_setting.src_y = 0;
    drm_device.planes_setting.src_w = drm_device.disp_image->width;
    drm_device.planes_setting.src_h = drm_device.disp_image->height;

    ret = drm_set_plane(&drm_device);
    if (ret < 0)
        goto drm_err;

    pthread_create(&drm_device.drm_flip_thread, NULL, drm_event_thread_func, &drm_device);

    /* ========================= drm module end ========================= */

    /* ========================= camera module start ========================= */
    CLEAR(mipi_video);
    mipi_video.width       = MIPI_CAM_WIDTH;
    mipi_video.height      = MIPI_CAM_HEIGHT;
    mipi_video.pixelformat = MIPI_CAM_PIXELFORMAT;

    mipi_cam_register(&mipi_video);
    LOG_INFO("mipi_cam_register success");

    ret = all_video_init();
    if (ret < 0) {
        LOG_ERROR("all_video_init fail");
        goto video_err;
    }

    ret = all_video_start();
    if (ret < 0) {
        LOG_ERROR("all_video_start fail");
        goto video_err;
    }

    mipi_video.thread_run = 1;
    pthread_create(&mipi_video.thread, NULL, mipi_thread_func, &mipi_video);

    signal(SIGINT, signal_head);

    LOG_INFO("Process start...");
    LOG_INFO("Press Ctrl + C to exit");
    while (!quit) {
        sleep(10);
    }
    LOG_INFO("Process exit");
    /* ========================= camera module end ========================= */

video_err:
    mipi_video.thread_run = 0;
    pthread_join(mipi_video.thread, NULL);
    mipi_video_opr->dev_exit(&mipi_video);
drm_err:
    drm_device.drm_run = 0;
    pthread_join(drm_device.drm_flip_thread, NULL);
    drm_exit(&drm_device);
rknn_err:
    rknn_destroy();

    return 0;
}
